package com.it.boot.controller;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.event.SyncReadListener;
import com.baomidou.mybatisplus.core.conditions.AbstractLambdaWrapper;
import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
import com.baomidou.mybatisplus.core.conditions.ISqlSegment;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.interfaces.Compare;
import com.baomidou.mybatisplus.core.conditions.interfaces.Func;
import com.baomidou.mybatisplus.core.conditions.interfaces.Join;
import com.baomidou.mybatisplus.core.conditions.interfaces.Nested;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.Query;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.mapper.Mapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.conditions.AbstractChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.ChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.ChainQuery;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.ChainUpdate;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.it.boot.config.ApiResult;
import com.it.boot.config.excel.*;
import com.it.boot.config.log.BizLog;
import com.it.boot.entity.UserEntity;
import com.it.boot.entity.dto.IdVo;
import com.it.boot.entity.dto.UserDto;
import com.it.boot.entity.qo.UserQo;
import com.it.boot.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.transaction.Transactional;
import java.io.IOException;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;


@Tag(name = "测试/用户MyBatisPlus")
@RestController
@RequestMapping("/api/UserBatisController")
@Slf4j
public class UserBatisController {
    @Resource
    private UserService userService;
    @Resource
    private UserImportListener listener;

    @Operation(summary = "新增")
    @PostMapping("/create")
    public ApiResult<Boolean> create(@RequestBody UserEntity userEntity) {
        return ApiResult.success(userService.save(userEntity));
    }

    @Operation(summary = "分页查询")
    @GetMapping("/getPage")
    public ApiResult<IPage<UserEntity>> getPage(Page<UserEntity> page, UserEntity userEntity) {
        return ApiResult.success(userService.getPage(page, userEntity));
    }

    @Operation(summary = "测试batis自动计算总数逻辑")
    @GetMapping("/batisPage")
    public ApiResult<IPage<UserEntity>> batisPage(Page<UserEntity> page, UserEntity userEntity) {
        return ApiResult.success(userService.batisPage(page, userEntity));
    }

    @BizLog(title = "编辑用户", type = "102")
    @Operation(summary = "根据id修改")
    @PostMapping("/updateById")
    public ApiResult<Boolean> updateById(@RequestBody UserEntity userEntity) {
        return ApiResult.success(userService.updateById(userEntity));
    }

    @Operation(summary = "根据id删除")
    @PostMapping("/deleteById")
    public ApiResult<Boolean> deleteById(@RequestBody Serializable id) {
        return ApiResult.success(userService.removeById(id));
    }

    @Operation(summary = "根据id批量删除")
    @PostMapping("/deleteByIds")
    public ApiResult<Boolean> deleteByIds(@RequestBody List<Integer> ids) {
        return ApiResult.success(userService.removeByIds(ids));
    }

    @Operation(summary = "连表")
    @GetMapping("/join")
    public ApiResult<IPage<UserEntity>> join(Page<UserEntity> page, List<Integer> ids) {
        return userService.join(page, ids);
    }

    @Operation(summary = "mybatis查询返回dto")
    @GetMapping("/mpjDtoProjection")
    public ApiResult<List<UserDto>> mpjDtoProjection(IdVo idVo) {
        MPJLambdaWrapper<UserEntity> wrapper = new MPJLambdaWrapper<>();
        wrapper.eq(UserEntity::getId, idVo.getId());
        // wrapper.selectAs();
        // wrapper.leftJoin();
        // wrapper.selectFunc()
        // wrapper.selectAll()
        List<UserDto> res = userService.getBaseMapper().selectJoinList(UserDto.class, wrapper);
        return ApiResult.success(res);
    }

    @Operation(summary = "测试mybatis 注解查询")
    @GetMapping("/testSelect")
    public ApiResult<UserEntity> testSelect(Integer id) {
        return ApiResult.success(userService.getUserById(id));
    }


    /**
     * <pre>
     * @see Query
     * @see     QueryWrapper                    拼接sql
     * @see         QueryChainWrapper               拼接sql chain(??)
     * @see     LambdaQueryChainWrapper
     * @see ChainWrapper
     * @see     ChainQuery                      执行query (list page one count)
     * @see         LambdaQueryWrapper              拼接sql lambdaChain
     * @see         QueryChainWrapper
     * @see     ChainUpdate
     * @see ISqlSegment
     * @see     Wrapper
     * @see         AbstractWrapper
     * @see             QueryWrapper
     * @see             AbstractLambdaWrapper
     * @see                 LambdaQueryWrapper
     * @see         AbstractChainWrapper
     * @see             LambdaQueryChainWrapper
     * @see             QueryChainWrapper
     * @see Mapper
     * @see     BaseMapper                     执行 insert delete selectById selectList(条件)
     * @see Join( Nested , Func , Compare )    and or isNull eq nest in
     * @see     AbstractWrapper
     * @see     AbstractChainWrapper
     *
     *         // QueryChainWrapper select 使用 字符串,可以拼接函数,自定义字段
     *         // LambdaQueryChainWrapper select 使用SFunction, 自定义字段??
     *         // 继承 AbstractChainWrapper方法, 众多sql字符相关,不能使用(getSqlSelect,getSqlComment,getTargetSql,getSqlSet,getCustomSqlSegment)
     * </pre>
     */
    @Operation(summary = "测试 QueryWrapper 复杂查询")
    @GetMapping("/testQueryWrapper")
    public void testQueryWrapper() {
        QueryWrapper<UserEntity> wrapper = new QueryWrapper<>();
        wrapper.select("id");
        log.info("Wrapper/QueryWrapper 获取sql: {}", wrapper.getExpression().getSqlSegment());
        // wrapper.isEmptyOfWhere();
        // wrapper.nonEmptyOfWhere();
        // wrapper.isEmptyOfNormal();
        // wrapper.nonEmptyOfNormal();
        // wrapper.nonEmptyOfEntity();
        //
        // wrapper.getExpression();
        // wrapper.getExpression().getSqlSegment();
        // wrapper.getExpression().getNormal();
        // wrapper.getExpression().getGroupBy();
        // wrapper.getExpression().getHaving();
        //
        // wrapper.getSqlFirst();
        // wrapper.getSqlComment();
        // wrapper.getSqlSelect();
        // wrapper.getTargetSql();
        // wrapper.getSqlSet();
        // wrapper.getCustomSqlSegment();
        // wrapper.getEntity();

        userService.list(wrapper);
    }

    @Operation(summary = "测试 LambdaQueryWrapper 复杂查询")
    @GetMapping("/testLambdaQueryWrapper")
    public void testLambdaQueryWrapper() {
        LambdaQueryWrapper<UserEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(UserEntity::getId);
        wrapper.getExpression();
        log.info("Wrapper/LambdaQueryWrapper 获取sql: {}", wrapper.getExpression().getSqlSegment());

        userService.list(wrapper);
    }

    @Operation(summary = "测试 QueryChainWrapper 复杂查询")
    @GetMapping("/testQueryChainWrapper")
    public void testQueryChainWrapper() {
        QueryChainWrapper<UserEntity> wrapper = new QueryChainWrapper<>(userService.getBaseMapper());
        wrapper.select("id");

        log.info("ChainWrapper/QueryChainWrapper 获取sql: {}", wrapper.getWrapper().getExpression().getSqlSegment());

        userService.list(wrapper);
    }

    @Operation(summary = "测试 LambdaQueryChainWrapper 复杂查询")
    @GetMapping("/testLambdaQueryChainWrapper")
    public ApiResult<List<UserEntity>> testLambdaQueryChainWrapper(UserQo qo) {
        LambdaQueryChainWrapper<UserEntity> wrapper = userService.lambdaQuery();
        log.info("ChainWrapper/LambdaQueryChainWrapper 获取sql: {}", wrapper.getWrapper()
                .getExpression()
                .getSqlSegment());

        Consumer<LambdaQueryWrapper<UserEntity>> whereAuth = w -> w.or(w2 -> w2.eq(UserEntity::getCreateBy, 1)
                .eq(UserEntity::getCreateBy, 2)).or(w2 -> w2.eq(UserEntity::getDeptId, 1).eq(UserEntity::getDeptId, 2));

        Consumer<LambdaQueryWrapper<UserEntity>> whereSearch = w -> w.like(UserEntity::getUserName, qo.getSearch())
                .or()
                .like(UserEntity::getNickName, qo.getSearch());
        wrapper.select(UserEntity::getId, UserEntity::getUserName, UserEntity::getPhone)
                .isNotNull(true, UserEntity::getUserName)
                .leSql(true, UserEntity::getId, "10") //  id < 10
                .inSql(true, UserEntity::getId, "1,2") // id in (1, 2)
                .nested(true, w -> w.eq(UserEntity::getId, "1").or().eq(UserEntity::getId, "2"))
                .func(true, w -> w.eq(UserEntity::getId, "5"))
                .apply(true, "1=1", 1)
                // .exists(true, "exists_Sql3", 1, 2)
                // .allEq(new HashMap<>())
                .and(true, whereSearch)
                .and(true, whereAuth)
                .groupBy(true, UserEntity::getId)
                // .having(true, "having_sql4")
                .orderBy(true, false, UserEntity::getCreateTime)
                // .first("# first \n")
                .last("# last \n")
                .comment("# this is comment\n");

        List<UserEntity> res = wrapper.list();
        return ApiResult.success(res);
    }

    /**
     * 自定义 Listener, 支持校验
     */
    @Transactional
    @Operation(summary = "导入Excel")
    @GetMapping("/importExcel")
    public ApiResult<ImportInfo> importExcel(@RequestParam("file") MultipartFile file) throws IOException {

        MyExcelUtils.validExcelFile(file);


        // 读取Excel文件
        EasyExcel.read(file.getInputStream(), UserImportVO.class, listener)
                .sheet()
                .headRowNumber(1) // 跳过表头
                .doRead();

        // 获取导入结果
        List<ImportError> errorList = listener.getErrorList();
        int successCount = listener.getSuccessCount();
        int errorCount = listener.getErrorCount();

        // 生成错误报告（如果有错误数据）
        if (!errorList.isEmpty()) {
            // 生成错误报告文件，返回文件路径
            // 可以使用EasyExcel将错误数据写入新的Excel文件，并在最后一列添加错误信息
            String fileName1 = "error_report_" + System.currentTimeMillis() + ".xlsx";
            // EasyExcel.write(fileName, UserImportVO.class)
            //         .sheet("错误数据")
            //         .doWrite(errorList);
            return ApiResult.fail(null, ImportInfo.fail(successCount, errorCount, fileName1, "存在数据校验错误"));
        }

        return ApiResult.success(ImportInfo.success(successCount, "导入成功"));
    }

    /**
     * SyncReadListener 无校验, 一次性读入内存
     */
    @Transactional
    @Operation(summary = "testSyncReadListener")
    @PostMapping("/testSyncReadListener")
    public ApiResult<Object> testSyncReadListener(@RequestParam("file") MultipartFile file) throws IOException {
        SyncReadListener listener = new SyncReadListener();
        MyExcelUtils.validExcelFile(file);

        EasyExcel.read(file.getInputStream(), UserImportVO.class, listener).sheet().doRead();
        List<Object> allData = listener.getList();
        List<UserImportVO> dtos = allData.stream()
                .map(obj -> (UserImportVO) obj)
                .collect(Collectors.toList());

        dtos.forEach(dto -> {
            // 手动注解校验
            // 数据库验证
            // 保存数据库
            UserEntity entity = new UserEntity();
            BeanUtils.copyProperties(dto, entity);
            userService.save(entity);
        });
        return ApiResult.success();
    }

    @Operation(summary = "下载模板")
    @GetMapping("/templateExcel")
    public void templateExcel(HttpServletResponse response) {
        MyExcelUtils.exportExcel(response, "用户管理-导入模版.xlsx", Collections.emptyList(), UserImportVO.class);
    }

    @Operation(summary = "导出Excel")
    @GetMapping("/exportExcel")
    public void exportExcel(HttpServletResponse response, @Parameter(description = "查询条件") UserEntity entity) {
        IPage<UserEntity> res = userService.getPage(Page.of(1, -1), entity);
        List<UserExportVO> dos = res.getRecords().stream().map(user -> {
            UserExportVO vo = new UserExportVO();
            vo.setId(user.getId());
            vo.setAvatar(user.getAvatar());
            vo.setPhone(user.getPhone());
            vo.setEmail(user.getEmail());
            vo.setUsername(user.getUserName());
            vo.setCreateTime(user.getCreateTime());

            vo.setAmount(123456.123456);
            vo.setProcess(BigDecimal.valueOf(0.2));
            vo.setDate(new Date());
            return vo;
        }).collect(Collectors.toList());
        MyExcelUtils.exportExcel(response, "用户数据.xlsx", dos, UserExportVO.class);
    }
}